#include "Palette.h"

namespace ECS
{

	AssemblyDef::AssemblyDef( MetaClass* assemblyClass ) : _assemblyClass(assemblyClass)
	{
		assert(assemblyClass->isSubclass<Assembly>());
	}

	void AssemblyDef::init( Assembly* assembly )
	{
		std::vector<std::pair<Component*, ComponentDef*> > components;
		components.reserve(_components.size());

		// create new components
		for (auto it = _components.begin(); it != _components.end(); ++it)
		{
			ComponentDef* def = *it;
			Component* c = assembly->getComponent(def->getComponentClass()->typeID);
			if (c == nullptr)
			{
				c = def->create();
				assembly->addComponent(c);
			}

			components.push_back(std::make_pair(c, def));
		}

		// init all created components
		for (auto it = components.begin(); it != components.end(); ++it)
		{
			it->second->init(it->first);
		}
	}

	AssemblyDef* AssemblyDef::clone()
	{
		AssemblyDef* a = new AssemblyDef(_assemblyClass);

		for (size_t i = 0; i < _components.size(); ++i)
		{
			a->_components.push_back(_components[i]->clone());
		}
		return a;
	}

	void AssemblyDef::addOrMerge( ComponentDef * componentDef )
	{
		for (size_t i = 0; i < _components.size(); ++i)
			if (_components.at(i)->getComponentClass() == componentDef->getComponentClass())
			{
				_components[i]->merge(componentDef);
				return;
			}
		addComponent(componentDef);
	}

	void AssemblyDef::addComponent( ComponentDef* def )
	{
		_components.push_back(def);
	}

	Assembly* ECS::AssemblyDef::create()
	{
		auto ctr  = _assemblyClass->getConstructor();
		Assembly* result = (Assembly*)(ctr->call());

		init(result);
		return result;
	}

	void Palette::addTemplate( const std::string& name, AssemblyDef* def )
	{
		_palette[name] = def;
	}

	Assembly* Palette::create( const std::string& name )
	{
		PaletteMap::iterator it = _palette.find(name);
		if (it == _palette.end())
			return nullptr;
		else
			return it->second->create();
	}

	AssemblyDef* Palette::getDef( const std::string& name )
	{
		PaletteMap::iterator it = _palette.find(name);
		if (it == _palette.end())
			return nullptr;
		else
			return it->second;
	}


	Component* ComponentDef::create()
	{
		auto ctr = _componentClass->getConstructor();
		if (ctr)
			return (Component*)ctr->call();

		return nullptr;
	}

	bool ComponentDef::init( Component* c )
	{
		auto initializer = _componentClass->getMethod("initWith");
		if (initializer)
		{
			void* args[] = {&_def};
			initializer->callWithArgs(c, args);
			return true;
		}
		return false;
	}

	ComponentDef::ComponentDef( MetaClass* cClass, MetaClass* dClass, void* def ) :
	_componentClass(cClass),
		_defClass(dClass),
		_def(def)
	{

	}

	ComponentDef* ComponentDef::clone()
	{
		void* newDef = _defClass->clone(_def);
		ComponentDef* c = new ComponentDef(_componentClass, _defClass, newDef);

		return c;
	}

	MetaClass* ComponentDef::getComponentClass() const
	{
		return _componentClass;
	}

	void ComponentDef::merge( ComponentDef* other )
	{
		assert(_defClass == other->_defClass);
		_defClass->cloneMembers(other->_def, _def);
	}

}
